﻿using System;
using System.Linq;
using System.Reflection.Emit;
using msilgc.cbuilder.CSyntaxTree;
using msilgc.cbuilder.MSILSyntaxTree;
using msilgc.CSyntaxTree;

namespace msilgc.cbuilder.CBuilder
{
    internal class LoadArgumentFactory : ITranslator {
        private readonly VariableFactory _varFactory;

        public LoadArgumentFactory(VariableFactory varFactory) {
            _varFactory = varFactory;
        }

        private static int ToInt16(byte[] data) {
            return (data[0] << 0x0) | (data[1] << 0x8);
        }

        private static int ToInt8(byte[] data) {
            return (data[0] << 0x0);
        }

        private static IVariable FindVariable(ICMethodBody methodBody, Instruction inst) {
            if(inst.Inst == OpCodes.Ldarg_0) {
                return methodBody.Arguments.ElementAt(0);
            }
            if(inst.Inst == OpCodes.Ldarg_1) {
                return methodBody.Arguments.ElementAt(1);
            }
            if(inst.Inst == OpCodes.Ldarg_2) {
                return methodBody.Arguments.ElementAt(2);
            }
            if(inst.Inst == OpCodes.Ldarg_3) {
                return methodBody.Arguments.ElementAt(3);
            }
            if(inst.Inst == OpCodes.Ldarg) {
                int offset = ToInt16(inst.Data);
                return methodBody.Arguments.ElementAt(offset);
            }
            if(inst.Inst == OpCodes.Ldarg_S) {
                int offset = ToInt8(inst.Data);
                return methodBody.Arguments.ElementAt(offset);
            }
            throw new Exception("unknown instruction: " + inst.ToString());
        }

        public ICStatement TranslateAndAdd(CMethodBody methodBody, CBasicBlock currentBlock, BasicBlock block, Instruction instruction) {
            IVariable source = FindVariable(methodBody, instruction);
            IVariable target = _varFactory.CreateNext(source.Type);
            AssignToLocalVariable stm = new AssignToLocalVariable(source, target);
            currentBlock.Statements.Add(stm);
            return stm;
        }
    }
}